package net.apexes.codegen.core;

import com.querydsl.codegen.utils.CodeWriter;
import com.querydsl.codegen.utils.model.Parameter;
import com.querydsl.codegen.utils.model.Type;
import net.apexes.commons.lang.Checks;
import net.apexes.commons.lang.Comparators;

import java.io.IOException;
import java.lang.annotation.Annotation;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.Set;
import java.util.TreeSet;
import java.util.function.Function;

class OrderableImportCodeWriter implements CodeWriter {

    private final CodeWriter writer;
    private final Set<String> classImports;
    private final Set<String> packageImports;

    OrderableImportCodeWriter(CodeWriter writer) {
        this.writer = writer;
        Comparator<String> comparator = (o1, o2) -> {
            if (o1.startsWith("java.")) {
                if (!o2.startsWith("java.")) {
                    return -1;
                }
                return Comparators.compare(o1, o2);
            } else if (o2.startsWith("java.")) {
                return 1;
            }

            if (o1.startsWith("javax.")) {
                if (!o2.startsWith("javax.")) {
                    return -1;
                }
                return Comparators.compare(o1, o2);
            } else if (o2.startsWith("javax.")) {
                return 1;
            }

            return Comparators.compare(o1, o2);
        };
        this.classImports = new TreeSet<>(comparator);
        this.packageImports = new TreeSet<>(comparator);
    }

    void writeImport() throws IOException {
        if (Checks.isNotEmpty(packageImports)) {
            writer.importPackages(packageImports.toArray(new String[0]));
        }
        if (Checks.isNotEmpty(classImports)) {
            writer.importClasses(classImports.toArray(new String[0]));
        }
    }

    @Override
    public String getRawName(Type type) {
        return writer.getRawName(type);
    }

    @Override
    public String getGenericName(boolean asArgType, Type type) {
        return writer.getGenericName(asArgType, type);
    }

    @Override
    public String getClassConstant(String className) {
        return writer.getClassConstant(className);
    }

    @Override
    public CodeWriter annotation(Annotation annotation) throws IOException {
        return writer.annotation(annotation);
    }

    @Override
    public CodeWriter annotation(Class<? extends Annotation> annotation) throws IOException {
        return writer.annotation(annotation);
    }

    @Override
    public CodeWriter beginClass(Type type) throws IOException {
        return writer.beginClass(type);
    }

    @Override
    public CodeWriter beginClass(Type type, Type superClass, Type... interfaces) throws IOException {
        return writer.beginClass(type, superClass, interfaces);
    }

    @Override
    public <T> CodeWriter beginConstructor(Collection<T> params, Function<T, Parameter> transformer) throws IOException {
        return writer.beginConstructor(params, transformer);
    }

    @Override
    public CodeWriter beginConstructor(Parameter... params) throws IOException {
        return writer.beginConstructor(params);
    }

    @Override
    public CodeWriter beginInterface(Type type, Type... interfaces) throws IOException {
        return writer.beginInterface(type, interfaces);
    }

    @Override
    public CodeWriter beginLine(String... segments) throws IOException {
        return writer.beginLine(segments);
    }

    @Override
    public <T> CodeWriter beginPublicMethod(Type returnType, String methodName, Collection<T> parameters, Function<T, Parameter> transformer) throws IOException {
        return writer.beginPublicMethod(returnType, methodName, parameters, transformer);
    }

    @Override
    public CodeWriter beginPublicMethod(Type returnType, String methodName, Parameter... args) throws IOException {
        return writer.beginPublicMethod(returnType, methodName, args);
    }

    @Override
    public <T> CodeWriter beginStaticMethod(Type type, String name, Collection<T> params, Function<T, Parameter> transformer) throws IOException {
        return writer.beginStaticMethod(type, name, params, transformer);
    }

    @Override
    public CodeWriter beginStaticMethod(Type returnType, String methodName, Parameter... args) throws IOException {
        return writer.beginStaticMethod(returnType, methodName, args);
    }

    @Override
    public CodeWriter end() throws IOException {
        return writer.end();
    }

    @Override
    public CodeWriter field(Type type, String name) throws IOException {
        return writer.field(type, name);
    }

    @Override
    public CodeWriter imports(Class<?>... classes) throws IOException {
        for (Class<?> cl : classes) {
            classImports.add(cl.getName());
        }
        return this;
    }

    @Override
    public CodeWriter imports(Package... packages) throws IOException {
        for (Package p : packages) {
            packageImports.add(p.getName());
        }
        return this;
    }

    @Override
    public CodeWriter importClasses(String... classes) throws IOException {
        classImports.addAll(Arrays.asList(classes));
        return this;
    }

    @Override
    public CodeWriter importPackages(String... packages) throws IOException {
        packageImports.addAll(Arrays.asList(packages));
        return this;
    }

    @Override
    public CodeWriter javadoc(String... lines) throws IOException {
        return writer.javadoc(lines);
    }

    @Override
    public CodeWriter line(String... segments) throws IOException {
        return writer.line(segments);
    }

    @Override
    public CodeWriter nl() throws IOException {
        return writer.nl();
    }

    @Override
    public CodeWriter packageDecl(String packageName) throws IOException {
        return writer.packageDecl(packageName);
    }

    @Override
    public CodeWriter privateField(Type type, String name) throws IOException {
        return writer.privateField(type, name);
    }

    @Override
    public CodeWriter privateFinal(Type type, String name) throws IOException {
        return writer.privateField(type, name);
    }

    @Override
    public CodeWriter privateFinal(Type type, String name, String value) throws IOException {
        return writer.privateFinal(type, name, value);
    }

    @Override
    public CodeWriter privateStaticFinal(Type type, String name, String value) throws IOException {
        return writer.privateStaticFinal(type, name, value);
    }

    @Override
    public CodeWriter protectedField(Type type, String name) throws IOException {
        return writer.protectedField(type, name);
    }

    @Override
    public CodeWriter protectedFinal(Type type, String name) throws IOException {
        return writer.protectedField(type, name);
    }

    @Override
    public CodeWriter protectedFinal(Type type, String name, String value) throws IOException {
        return writer.protectedFinal(type, name, value);
    }

    @Override
    public CodeWriter publicField(Type type, String name) throws IOException {
        return writer.publicField(type, name);
    }

    @Override
    public CodeWriter publicField(Type type, String name, String value) throws IOException {
        return writer.publicField(type, name, value);
    }

    @Override
    public CodeWriter publicFinal(Type type, String name) throws IOException {
        return writer.publicFinal(type, name);
    }

    @Override
    public CodeWriter publicFinal(Type type, String name, String value) throws IOException {
        return writer.publicFinal(type, name, value);
    }

    @Override
    public CodeWriter publicStaticFinal(Type type, String name, String value) throws IOException {
        return writer.publicStaticFinal(type, name, value);
    }

    @Override
    public CodeWriter staticimports(Class<?>... imports) throws IOException {
        return writer.staticimports(imports);
    }

    @Override
    public CodeWriter suppressWarnings(String type) throws IOException {
        return writer.suppressWarnings(type);
    }

    @Override
    public CodeWriter suppressWarnings(String... types) throws IOException {
        return writer.suppressWarnings(types);
    }

    @Override
    public Appendable append(CharSequence csq) throws IOException {
        return writer.append(csq);
    }

    @Override
    public Appendable append(CharSequence csq, int start, int end) throws IOException {
        return writer.append(csq, start, end);
    }

    @Override
    public Appendable append(char c) throws IOException {
        return writer.append(c);
    }
}
